
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include <linux/mod_devicetable.h>
#include <linux/of_device.h>

#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>


static int major;
static struct class *ds18b20_class;
static struct gpio_desc *ds18b20_gpiod;
static unsigned char ds18b20_data[2];
//static int irq;



// static DECLARE_WAIT_QUEUE_HEAD(ds18b20_wait_queue);  			 /*创建并初始化等待队列*/

static int ds18b20_open(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static void ds18b20_delay_us(unsigned int delay_us)
{
	unsigned u64 pre_ns, last_ns;
	pre_ns = ktime_get_ns();
	while (1)
	{
		last_ns = ktime_get_ns();
		if (last_ns - pre_ns  >= delay_us *1000)
			break;
	}
}



static void ds18b20_reset(void)
{

	/*设置引脚为输出，并拉高电平20ms,同时作为复位信号*/
	gpiod_direction_output(ds18b20_gpiod, 1);
	mdelay(20);
	
	/*拉低电平480us*/
	gpiod_set_value(ds18b20_gpiod, 0);
	ds18b20_delay_us(480);

	/*设置引脚为输入，上拉电阻拉高电平30us*/
	gpiod_direction_input(ds18b20_gpiod);	
	ds18b20_delay_us(30);
}



static int wait_for_ds18b20_ack(void)
{
	int timeout_us = 1000;

	/*等待低电平出现*/
	
	while (gpiod_get_value(ds18b20_gpiod) && timeout_us)
	{
		udelay(1);
		timeout_us--;		
	}
	if (timeout_us == 0)
	{
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}

	/*等待高电平*/
	timeout_us = 1000;
	while (!gpiod_get_value(ds18b20_gpiod) && timeout_us)
	{
		udelay(1);
		timeout_us--;
	}
	
	if (timeout_us == 0)
	{
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}
	
	return 0;
}


static void ds18b20_write_byte(unsigned char data)
{
	int i;
	gpiod_direction_output(ds18b20_gpiod, 1);
	for (i = 0; i < 8; i++)
	{
		if (data & (1<<i))
		{
			gpiod_set_value(ds18b20_gpiod, 0);
			ds18b20_delay_us(2);
			gpiod_set_value(ds18b20_gpiod, 1);
			ds18b20_delay_us(70);
		}
		else 
		{
			gpiod_set_value(ds18b20_gpiod, 0);
			ds18b20_delay_us(70);
			gpiod_set_value(ds18b20_gpiod, 1);
			ds18b20_delay_us(10);
		}
	}

}


static unsigned char ds18b20_read_byte(void)
{
	int i;
	int val;
	unsigned char data = 0;
	gpiod_direction_output(ds18b20_gpiod, 1);

	for (i = 0; i < 8; i++ )
	{
		
		gpiod_set_value(ds18b20_gpiod, 0);
		ds18b20_delay_us(2);
		gpiod_direction_input(ds18b20_gpiod);
		ds18b20_delay_us(5);
		if (gpiod_get_value(ds18b20_gpiod))
			data |= (1<<i);

		ds18b20_delay_us(60);
		gpiod_direction_output(ds18b20_gpiod, 1);

	}

	return data;
}




static ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offsize)
{
	unsigned int tmp;
	int ret;
	unsigned char tempL, tempH;
	unsigned char integer;
	unsigned char decimal1, decimal2, decimal;
	unsigned long flags;
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);

	if (size != 2)
		return -EINVAL;
	
	local_irq_save(flags);
	
	/*主机发送复位信号*/
	ds18b20_reset();

	/*等待从机回应*/
	ret = wait_for_ds18b20_ack();
	if (ret)	
	{
		local_irq_restore(flags);
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}

	/*发送“0xCC”命令忽略选择哪个温度传感器*/
	ds18b20_write_byte(0xCC);

	/*发送“0x44”启动温度采集转换*/
	ds18b20_write_byte(0x44);

	/*主机控制数据总线保持高电平1s, 阻塞睡眠1s等待转换*/
	local_irq_restore(flags);  //开中断
	
	gpiod_direction_output(ds18b20_gpiod, 1);
	set_current_state(TASK_INTERRUPTIBLE);
	schedule_timeout(HZ);
	
	local_irq_save(flags); //关中断
	/*主机发送复位信号*/
	ds18b20_reset();

	/*等待从机回应*/
	ret = wait_for_ds18b20_ack();
	if (ret)	
	{
		local_irq_restore(flags);
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}


	/*发送“0xcc”命令忽略选择哪个温度传感器*/
	ds18b20_write_byte(0xCC);

	/*发送“0xBE”启动读命令*/
	ds18b20_write_byte(0xBE);

	/*获取2字节温度数据*/
	tempL = ds18b20_read_byte();
	tempH = ds18b20_read_byte();
	local_irq_restore(flags);
	
	if (tempH > 0x7f)
	{
		tmp = tempH << 8 | tempL;
		tmp = ~tmp + 1;
		tempH = tmp >> 8 & 0xFF;
		tempL = tmp & 0xFF;
		integer = tempL / 16 + tempH *16;
		decimal1 = (tempL & 0x0F) * 10 / 16;
		decimal2 = (tempL & 0x0F) * 100 / 16 % 10;
		decimal = decimal1 *10 + decimal2;
		ds18b20_data[0] = integer;
		ds18b20_data[1] = decimal;
		ret = copy_to_user(buf, ds18b20_data, 2);
		return 2;
	}
	
	integer = tempL / 16 + tempH *16;
	decimal1 = (tempL & 0x0F) * 10 / 16;
	decimal2 = (tempL & 0x0F) * 100 / 16 % 10;
	decimal = decimal1 *10 + decimal2;
	ds18b20_data[0] = integer;
	ds18b20_data[1] = decimal;
	ret = copy_to_user(buf, ds18b20_data, 2);
	return 2;

}

static int ds18b20_close(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations ds18b20_ops = {
	.owner  = THIS_MODULE,
	.open    = ds18b20_open,
	.release = ds18b20_close,
	.read    = ds18b20_read,
};

#if 0
static irqreturn_t ds18b20_isr(int num, void *dev)
{
	int val = gpiod_get_value(ds18b20_echo);
	if (val)           /*回应引脚高电平*/
		ds18b20_data_ns = ktime_get_ns();
	else
	{
		ds18b20_data = ktime_get_ns() - ds18b20_data_ns;   /*回应引脚高电平*/
		wake_up(&ds18b20_wait_queue);
	}
		
	return IRQ_HANDLED;
}

#endif



static struct of_device_id of_ds18b20_match_table[] = {
	{ .compatible = "zmq,ds18b20", },
	{ },
};

static int ds18b20_probe(struct platform_device *pdev)
{

	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	ds18b20_gpiod = gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH);

	//irq = gpiod_to_irq(ds18b20_echo);
	//request_irq(irq, ds18b20_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "zmq_ds18b20_irq", NULL);
	
	device_create(ds18b20_class, NULL, MKDEV(major, 0), NULL, "zmq_ds18b20");
	return 0;
	
}


static int ds18b20_remove(struct platform_device *dev)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(ds18b20_class, MKDEV(major, 0));
	gpiod_put(ds18b20_gpiod);   //释放gpio
	//gpiod_put(ds18b20_echo);
	//free_irq(irq, NULL);
	
	return 0;
}


static struct platform_driver ds18b20_platform_drv = {
	.probe = ds18b20_probe,
	.remove = ds18b20_remove,
	.driver = 	{
		.name = "ds18b20",
		.of_match_table = of_ds18b20_match_table,
	},

};



static  int  ds18b20_init(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "zmq_ds18b20_chrdrv", &ds18b20_ops);
	ds18b20_class = class_create(THIS_MODULE, "zmq_ds18b20_class");
	if(IS_ERR(ds18b20_class))
	{
		unregister_chrdev(major, "zmq_ds18b20_chrdrv");
		return PTR_ERR(ds18b20_class);
	}
	platform_driver_register(&ds18b20_platform_drv);
	
	return 0;
}


static void ds18b20_exit(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);

	platform_driver_unregister(&ds18b20_platform_drv);
	class_destroy(ds18b20_class);
	unregister_chrdev(major, "zmq_ds18b20_chrdev");
}


module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_LICENSE("GPL");






